﻿using Core;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class HorizontalLoopGrid : MonoBehaviour, ICircularGrid
{
    public Vector2 cellSize;
    public Vector2 spacing;
    public RectOffset padding;

    private RectTransform rectTransform;
    private ScrollRect scrollRect;

    // 视口大小
    private Vector2 viewPortSize;
    // 每一行的格子数
    private int constraintCount;
    // 视口内显示的行数
    private int viewPortLineCnt;

    private int startLine;
    private int currLine;

    private int minAmount;
    private int amount = -1;
    private int totalLineCnt;

    private readonly List<RectTransform> children = new();

    private Action<int> updateFunc;
    private Action<int> selectFunc;

    public void SetUpdateFunc(Action<int> func) => updateFunc = func;
    public void SetSelectFunc(Action<int> func) => selectFunc = func;

    public int Init()
    {
        rectTransform = GetComponent<RectTransform>();

        var rectViewport = transform.parent.GetComponent<RectTransform>();
        viewPortSize = rectViewport.rect.size;

        scrollRect = transform.GetComponentInParent<ScrollRect>();
        scrollRect.onValueChanged.AddListener(OnScrollRectValueChangedHandler);

        scrollRect.horizontal = true;
        scrollRect.vertical = false;

        constraintCount = (int)((viewPortSize.y - padding.top - padding.bottom + spacing.y) / (cellSize.y + spacing.y));
        constraintCount = Mathf.Max(constraintCount, 1);
        viewPortLineCnt = Mathf.CeilToInt((viewPortSize.x - padding.left - padding.right + spacing.x) / (cellSize.x + spacing.x));
        viewPortLineCnt += 1;

        minAmount = constraintCount * viewPortLineCnt;

        var childCount = transform.childCount;
        for (int i = 0; i < childCount; i++)
        {
            var element = transform.GetChild(i);
            children.Add(element.GetComponent<RectTransform>());
        }

        if (childCount < minAmount)
        {
            var child = transform.GetChild(0);
            for (int i = childCount; i < minAmount; i++)
            {
                var element = GameObject.Instantiate(child, transform);
                children.Add(element.GetComponent<RectTransform>());
            }
        }

        for (int i = 0; i < children.Count; i++)
        {
            var element = children[i];
            int index = i;
            var btn = element.GetChild(0)?.GetComponent<Button>();
            btn?.onClick.AddListener(() => { OnClickHandler(index); });
            element.gameObject.name = $"cell_{i}";
        }

        return minAmount;
    }

    private void OnClickHandler(int i)
    {
        int start_idx = startLine * constraintCount;
        int index = i + start_idx - (start_idx % minAmount);
        if (index < start_idx)
        {
            index += minAmount;
        }
        selectFunc?.Invoke(index);
    }

    public void Update()
    {
        if (currLine == startLine)
        {
            return;
        }
        Scroll();
    }

    public void OnDestroy()
    {
        scrollRect.onValueChanged.RemoveListener(OnScrollRectValueChangedHandler);
    }

    private Vector2 lastData = -Vector2.one;
    private void OnScrollRectValueChangedHandler(Vector2 data)
    {
        if (lastData == data) return;
        lastData = data;

        if (transform.childCount < minAmount)
        {
            return;
        }
        Vector2 currentPos = rectTransform.anchoredPosition;
        currLine = Mathf.FloorToInt((-currentPos.x - padding.left + spacing.x) / (cellSize.x + spacing.x));
        currLine = Mathf.Max(0, currLine);
    }

    public void SetAmount(int amount)
    {
        if (this.amount == amount)
        {
            Refresh();
            return;
        }

        this.amount = amount;

        startLine = currLine = 0;

        ResizeBG();
        Refresh();

        scrollRect.horizontalNormalizedPosition = 0;
    }

    public void SetNormalizedPosition(float value)
    {
        scrollRect.horizontalNormalizedPosition = value;
    }

    private void ResizeBG()
    {
        totalLineCnt = Mathf.CeilToInt(amount * 1.0f / constraintCount);

        float width = cellSize.x * totalLineCnt + spacing.x * (totalLineCnt - 1) + padding.left + padding.right;
        float height = cellSize.y * constraintCount + spacing.y * (constraintCount - 1) + padding.top + padding.bottom;
        rectTransform.sizeDelta = new Vector2(width, height);
    }

    private void Refresh()
    {
        for (int i = startLine; i < startLine + viewPortLineCnt; i++)
        {
            RefreshLine(i);
        }
    }

    private void Scroll()
    {
        int row;
        if (currLine > startLine)
        {
            row = startLine + viewPortLineCnt;
            startLine++;
        }
        else
        {
            row = startLine - 1;
            startLine--;
        }

        RefreshLine(row);
    }

    private void RefreshLine(int row)
    {
        for (int col = 0; col < constraintCount; col++)
        {
            int idx = row * constraintCount + col;
            var element = children[idx % children.Count];
            SetActive(element.gameObject, idx < amount);

            float x = padding.left + cellSize.x * row + spacing.x * row;
            float y = padding.top + cellSize.y * col + spacing.y * col;
            element.GetComponent<RectTransform>().anchoredPosition = new Vector2(x, -y);

            if (idx < amount)
            {
                updateFunc?.Invoke(idx);
            }
        }
    }

    public void SetActive(GameObject go, bool active)
    {
        if (go == null || go.activeSelf == active) return;

        go.SetActive(active);
    }
}
